package com.kk.xhr.model.service.impl;

import com.kk.xhr.common.ErrorCodeEnum;
import com.kk.xhr.common.RedisConstant;
import com.kk.xhr.common.pojo.UserStatusEnum;
import com.kk.xhr.model.assembler.UserStructKit;
import com.kk.xhr.model.dao.UserDynamicSqlSupport;
import com.kk.xhr.model.dao.UserMapper;
import com.kk.xhr.model.dto.PageDto;
import com.kk.xhr.model.dto.UserDTO;
import com.kk.xhr.model.dto.UserInfoDTO;
import com.kk.xhr.model.dto.UserQueryRequestDTO;
import com.kk.xhr.model.entity.User;
import com.kk.xhr.model.service.IUserService;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.tuple.Pair;
import org.mybatis.dynamic.sql.render.RenderingStrategies;
import org.mybatis.dynamic.sql.select.render.SelectStatementProvider;
import org.mybatis.dynamic.sql.where.AbstractWhereDSL;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.stereotype.Repository;

import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

import static org.mybatis.dynamic.sql.SqlBuilder.isEqualTo;
import static org.mybatis.dynamic.sql.SqlBuilder.isEqualToWhenPresent;
import static org.mybatis.dynamic.sql.SqlBuilder.isIn;
import static org.mybatis.dynamic.sql.SqlBuilder.isLikeWhenPresent;
import static org.mybatis.dynamic.sql.SqlBuilder.select;

/**
 * 用户接口实现
 *
 * @author kkmystery
 * @version 1.0 2021/2/28
 * @since 1.0.0
 */
@Repository
@RequiredArgsConstructor
public class UserServiceImpl implements IUserService {
    private final UserMapper userMapper;
    private final UserStructKit instance = UserStructKit.INSTANCE;

    /**
     * {@inheritDoc}
     */
    @Override
    public Optional<UserDTO> queryById(Long id) {
        return userMapper.selectByPrimaryKey(id).map(instance::toDto);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public User queryUser(Long id) {
        return userMapper.selectOne(dsl -> dsl
            .where(UserDynamicSqlSupport.id, isEqualTo(id))
            .and(UserDynamicSqlSupport.status, isEqualTo(UserStatusEnum.ACTIVE.getValue()))
            .limit(1)).get();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @Cacheable(value = RedisConstant.USER_CACHE, key = "#id")
    public Optional<UserDTO> queryNormal(Long id) {
        return userMapper.selectOne(dsl -> dsl
            .where(UserDynamicSqlSupport.id, isEqualTo(id))
            .and(UserDynamicSqlSupport.status, isEqualTo(UserStatusEnum.ACTIVE.getValue()))
            .limit(1))
            .map(instance::toDto);
    }

    /**
     * 根据用户id查询工号
     *
     * @param userId 用户id
     * @return 工号
     */
    @Override
    public Long queryWorkId(Long userId) {
        return userMapper.selectOne(select(UserDynamicSqlSupport.workId)
            .from(UserDynamicSqlSupport.user)
            .where(UserDynamicSqlSupport.id, isEqualTo(userId))
            .limit(1)
            .build().render(RenderingStrategies.MYBATIS3))
            .map(User::getWorkId).get();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Optional<UserDTO> queryByWorkId(Long workId) {
        return userMapper.selectOne(dsl -> dsl
            .where(UserDynamicSqlSupport.workId, isEqualTo(workId))
            .and(UserDynamicSqlSupport.status, isEqualTo(UserStatusEnum.ACTIVE.getValue()))
            .limit(1))
            .map(instance::toDto);
    }

    @Override
    @Cacheable(value = RedisConstant.USER_CACHE, key = "'workId:'+#workId")
    public Optional<Long> queryUserId(Long workId) {
        return userMapper.selectOne(dsl -> dsl
            .where(UserDynamicSqlSupport.workId, isEqualTo(workId))
            .and(UserDynamicSqlSupport.status, isEqualTo(UserStatusEnum.ACTIVE.getValue()))
            .limit(1))
            .map(User::getId);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @Cacheable(value = RedisConstant.USER_CACHE, key = "'name:'+#userId")
    public String queryName(Long userId) {
        return userMapper.selectOne(select(UserDynamicSqlSupport.name)
            .from(UserDynamicSqlSupport.user)
            .where(UserDynamicSqlSupport.id, isEqualTo(userId))
            .build().render(RenderingStrategies.MYBATIS3))
            .map(User::getName)
            .get();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public PageDto<UserDTO> queryPage(UserQueryRequestDTO dto) {
        long count = userMapper.count(dsl -> applyQueryArgs(dsl.where(), dto));
        if (0 == count) {
            return PageDto.empty();
        }
        return new PageDto<>(count, userMapper.select(dsl -> applyQueryArgs(dsl.where(), dto)
            .limit(dto.getLimit())
            .offset(dto.getOffset()))
            .stream()
            .map(instance::toDto)
            .collect(Collectors.toList()));
    }

    /**
     * 查询最新添加的用户工号
     *
     * @return 工号
     */
    @Override
    public Optional<User> queryLast() {
        return userMapper.selectOne(select(UserDynamicSqlSupport.workId)
            .from(UserDynamicSqlSupport.user)
            .orderBy(UserDynamicSqlSupport.id.descending())
            .limit(1)
            .build().render(RenderingStrategies.MYBATIS3));
    }

    /**
     * 根据部门id查询用户id
     *
     * @param departmentIds 部门id列表
     * @return 用户id集合
     */
    @Override
    public Set<Long> queryByDepartmentIds(List<Long> departmentIds) {
        if (departmentIds.isEmpty()) {
            return Collections.emptySet();
        }
        return userMapper.select(dsl -> dsl
            .where(UserDynamicSqlSupport.departmentId, isIn(departmentIds))
            .and(UserDynamicSqlSupport.status, isEqualTo(UserStatusEnum.ACTIVE.getValue())))
            .stream()
            .map(User::getId)
            .collect(Collectors.toSet());
    }

    /**
     * 添加用户
     *
     * @param dto UserDTO
     * @return 是否成功
     */
    @Override
    public Long add(UserDTO dto) {
        User user = instance.toEntity(dto);
        user.setPassword(user.getUsername());
        userMapper.insertSelective(user);
        return user.getId();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @Caching(evict = {
        @CacheEvict(value = RedisConstant.USER_CACHE, key = "#p0.id"),
        @CacheEvict(value = RedisConstant.USER_CACHE, key = "'workId:'+#p0.id"),
        @CacheEvict(value = RedisConstant.USER_CACHE, key = "'name:'+#p0.id")
    })
    public boolean modify(UserDTO dto) {
        if (null == dto.getId()) {
            return false;
        }
        return 1 == userMapper.updateByPrimaryKeySelective(instance.toEntity(dto));
    }

    /**
     * 修改用户状态
     *
     * @param id     主键id
     * @param status 状态
     * @return 是否成功
     */
    @Override
    public boolean modify(Long id, UserStatusEnum status) {
        return 1 == userMapper.updateByPrimaryKeySelective(new User().withId(id).withStatus(status.getValue()));
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Pair<ErrorCodeEnum, User> loginByPwd(String username, String password) {
        Optional<User> optional = userMapper.selectOne(dsl -> dsl
            .where(UserDynamicSqlSupport.username, isEqualTo(username))
            .and(UserDynamicSqlSupport.status, isEqualTo(UserStatusEnum.ACTIVE.getValue()))
            .limit(1));
        if (!optional.isPresent()) {
            return Pair.of(ErrorCodeEnum.USER_NOT_EXIST, null);
        }
        User user = optional.get();
        if (!password.equals(user.getPassword())) {
            return Pair.of(ErrorCodeEnum.PASSWORD_ERROR, null);
        }
        return Pair.of(ErrorCodeEnum.OK, user);
    }

    @Override
    public List<UserInfoDTO> query() {
        SelectStatementProvider provider = select(UserDynamicSqlSupport.id, UserDynamicSqlSupport.workId, UserDynamicSqlSupport.name)
            .from(UserDynamicSqlSupport.user)
            .where(UserDynamicSqlSupport.status, isEqualTo(UserStatusEnum.ACTIVE.getValue()))
            .build().render(RenderingStrategies.MYBATIS3);
        return userMapper.selectMany(provider)
            .stream()
            .map(entity -> UserInfoDTO.builder()
                .id(entity.getId())
                .workId(entity.getWorkId())
                .name(entity.getName())
                .email(entity.getEmail())
                .build())
            .collect(Collectors.toList());
    }

    @Override
    public List<UserDTO> queryByDepartmentId(Long id) {
        return userMapper.select(dsl -> dsl
            .where(UserDynamicSqlSupport.departmentId, isEqualTo(id))
            .and(UserDynamicSqlSupport.status, isEqualTo(UserStatusEnum.ACTIVE.getValue())))
            .stream()
            .map(instance::toDto)
            .collect(Collectors.toList());
    }

    @Override
    public void modifyPassword(Long userId, String password) {
        userMapper.updateByPrimaryKeySelective(new User().withId(userId).withPassword(password));
    }

    @Override
    public boolean existPosition(Long id) {
        return userMapper.selectOne(dsl -> dsl
            .where(UserDynamicSqlSupport.positionId, isEqualTo(id))
            .and(UserDynamicSqlSupport.status, isEqualTo(UserStatusEnum.ACTIVE.getValue()))
            .limit(1)).isPresent();
    }

    @Override
    public boolean existLevel(Long id) {
        return userMapper.selectOne(dsl -> dsl
            .where(UserDynamicSqlSupport.levelId, isEqualTo(id))
            .and(UserDynamicSqlSupport.status, isEqualTo(UserStatusEnum.ACTIVE.getValue()))
            .limit(1)).isPresent();
    }

    private <T extends AbstractWhereDSL> T applyQueryArgs(T where, UserQueryRequestDTO dto) {
        where.and(UserDynamicSqlSupport.name, isLikeWhenPresent(dto.getName()).then(str -> "%" + str + "%"))
            .and(UserDynamicSqlSupport.workId, isEqualToWhenPresent(dto.getWorkId()));
        if (null != dto.getStatus()) {
            where.and(UserDynamicSqlSupport.status, isEqualTo(dto.getStatus().getValue()));
        }
        return where;
    }
}
